using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using Jaws.Core.Web.Entities;
using Jaws.Core.Web.Entities.Roles;

namespace Jaws.Core.Web.Providers
{
    /// <summary>
    /// 
    /// </summary>
    public class XmlRoleProvider : RoleProvider
    {
        private string _applicationName;

        RoleCollection roles = XmlCollectionBase<RoleCollection, Role>.Load();

        /// <summary>
        /// Gets or sets the name of the application to store and retrieve role information for.
        /// </summary>
        /// <value></value>
        /// <returns>The name of the application to store and retrieve role information for.</returns>
        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The friendly name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        /// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
        /// <exception cref="T:System.InvalidOperationException">An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"></see> on a provider after the provider has already been initialized.</exception>
        /// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (String.IsNullOrEmpty(name))
            {
                name = "XmlRoleProvider";
            }

            base.Initialize(name, config);

            if (!String.IsNullOrEmpty(config["applicationName"]))
            {
                _applicationName = config["applicationName"];
            }
        }

        /// <summary>
        /// Adds the specified user names to the specified roles for the configured applicationName.
        /// </summary>
        /// <param name="usernames">A string array of user names to be added to the specified roles.</param>
        /// <param name="roleNames">A string array of the role names to add the specified user names to.</param>
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            foreach (string rName in roleNames)
            {
                Role role = roles.FindByName(rName);
                if (role != null)
                {
                    foreach (string uName in usernames)
                    {
                        if (!role.Users.Contains(uName))
                        {
                            role.Users.Add(uName);
                        }
                    }
                }
            }

            roles.Save();
        }

        /// <summary>
        /// Adds a new role to the data source for the configured applicationName.
        /// </summary>
        /// <param name="roleName">The name of the role to create.</param>
        public override void CreateRole(string roleName)
        {
            var role = new Role {Name = roleName};

            roles.Add(role);
            roles.Save();
        }

        /// <summary>
        /// Removes a role from the data source for the configured applicationName.
        /// </summary>
        /// <param name="roleName">The name of the role to delete.</param>
        /// <param name="throwOnPopulatedRole">If true, throw an exception if roleName has one or more members and do not delete roleName.</param>
        /// <returns>
        /// true if the role was successfully deleted; otherwise, false.
        /// </returns>
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            Role role = roles.FindByName(roleName);
            if (role != null)
            {
                if (throwOnPopulatedRole && role.Users.Count > 0)
                {
                    return false;
                }

                roles.Remove(role);
                roles.Save();
            }

            return true;
        }

        /// <summary>
        /// Gets an array of user names in a role where the user name contains the specified user name to match.
        /// </summary>
        /// <param name="roleName">The role to search in.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <returns>
        /// A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role.
        /// </returns>
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            Role role = roles.FindByName(roleName);
            var users = new List<string>();

            if (role != null)
            {
                foreach (string user in role.Users)
                {
                    if (user.Contains(usernameToMatch))
                    {
                        users.Add(user);
                    }
                }
            }

            return users.ToArray();
        }

        /// <summary>
        /// Gets a list of all the roles for the configured applicationName.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the roles stored in the data source for the configured applicationName.
        /// </returns>
        public override string[] GetAllRoles()
        {
            var roleList = new List<Role>(roles);

            List<string> names = roleList.ConvertAll(new Converter<Role, string>(ConvertRoleToString));
            return names.ToArray();
        }

        private static string ConvertRoleToString(Role role)
        {
            return role.Name;
        }

        /// <summary>
        /// Gets a list of the roles that a specified user is in for the configured applicationName.
        /// </summary>
        /// <param name="username">The user to return a list of roles for.</param>
        /// <returns>
        /// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
        /// </returns>
        public override string[] GetRolesForUser(string username)
        {
            var roleList = new List<string>();

            foreach (Role r in roles)
            {
                if (r.Users.Contains(username))
                {
                    roleList.Add(r.Name);
                }
            }

            return roleList.ToArray();
        }

        /// <summary>
        /// Gets a list of users in the specified role for the configured applicationName.
        /// </summary>
        /// <param name="roleName">The name of the role to get the list of users for.</param>
        /// <returns>
        /// A string array containing the names of all the users who are members of the specified role for the configured applicationName.
        /// </returns>
        public override string[] GetUsersInRole(string roleName)
        {
            Role role = roles.FindByName(roleName);

            string[] output = null;

            if (role != null)
            {
                output = new string[role.Users.Count];
                role.Users.CopyTo(output, 0);
            }

            return output;
        }

        /// <summary>
        /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
        /// </summary>
        /// <param name="username">The user name to search for.</param>
        /// <param name="roleName">The role to search in.</param>
        /// <returns>
        /// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
        /// </returns>
        public override bool IsUserInRole(string username, string roleName)
        {
            Role role = roles.FindByName(roleName);

            if (role != null)
            {
                return role.Users.Contains(username);
            }

            return false;
        }

        /// <summary>
        /// Removes the specified user names from the specified roles for the configured applicationName.
        /// </summary>
        /// <param name="usernames">A string array of user names to be removed from the specified roles.</param>
        /// <param name="roleNames">A string array of role names to remove the specified user names from.</param>
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {

            foreach (string rName in roleNames)
            {
                Role role = roles.FindByName(rName);
                if (role != null)
                {
                    foreach (string uName in usernames)
                    {
                        if (role.Users.Contains(uName))
                        {
                            role.Users.Remove(uName);
                        }
                    }
                }
            }

            roles.Save();
        }

        /// <summary>
        /// Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName.
        /// </summary>
        /// <param name="roleName">The name of the role to search for in the data source.</param>
        /// <returns>
        /// true if the role name already exists in the data source for the configured applicationName; otherwise, false.
        /// </returns>
        public override bool RoleExists(string roleName)
        {
            Role role = roles.FindByName(roleName);

            return role != null;
        }
    }
}